% Prototype C++ Code Auditor
% J.R. Cordy et al., Huawei Technologies

% November 2023 (Revised Nov 2023)

% This is a rapid prototype of a C++ code auditor tool to check and advise on proper HOS C++ code usage.

% C++ grammar
include "bom.grm"
include "cpp.grm"

% Distinguish audited references to avoid infinite loops
redefine unary_expression
        ...
    |   [not token] [audited_unary_expression]
end define

define audited_unary_expression
        [id] '( [unary_expression] ')
end define

% We work by first marking all dereferences as problematic,
% and then unmarking those that are guarded.

function main
    replace [program]
        P [program]
    by
        P %[auditExpressionDereferences]
          %[unmarkCheckNullVoids]
          %[unmarkIfNotNulls]
		  [auditNapiRules]
end function

% Arbitrary expression dereferences
rule auditExpressionDereferences
    replace $ [statement_seq]
        Statements [statement*]
    by
        Statements [checkExpressionDereferences]
end rule

rule auditNapiRules
        replace $ [statement*]
                Statement1 [statement]
                Statement2 [statement]
                MoreStatements [statement*]
        deconstruct Statement1
                'napi_create_object
        deconstruct Statement2
                'napi_define_properties 
        construct ResultStatement [statement]
                'napi_create_object_with_properties 
        by
                ResultStatement
                MoreStatements
end rule


rule checkExpressionDereferences
    skipping [audited_unary_expression]
    replace $ [unary_expression]
        PointerDereferenceExpression [unary_expression]
    where
        PointerDereferenceExpression [isStarDereferenceExpression] [isArrowDereferenceExpression]
    construct AuditedPointerDereferenceExpression [audited_unary_expression]
        'UNCHECKED_POINTER_DEREFERENCE '( PointerDereferenceExpression ')
    by
        AuditedPointerDereferenceExpression
end rule

function isStarDereferenceExpression
    match [unary_expression]
        '* _ [cast_expression]
end function

function isArrowDereferenceExpression
    match [unary_expression]
        _ [postfix_expression] '-> _ ['template ?] _ [id_expression]
end function

% If the dereference is guarded by a check-null-void, it's safe
rule unmarkCheckNullVoids
    replace $ [statement*]
        'CHECK_NULL_VOID '( CheckedPostfixExpression [postfix_expression] ') Semi [semi]
        Statements [statement*]
    by
        'CHECK_NULL_VOID '( CheckedPostfixExpression ') Semi
        Statements [unmarkAuditedUnaryExpression CheckedPostfixExpression]
end rule

rule unmarkAuditedUnaryExpression CheckedPostfixExpression [postfix_expression]
    replace $ [unary_expression]
        AuditedUnaryExpression [audited_unary_expression]
    deconstruct AuditedUnaryExpression
        _ [id] '( UnaryExpression [unary_expression] ')
    skipping [primary_expression] deconstruct * [postfix_expression] UnaryExpression
        CheckedPostfixExpression
    by
        UnaryExpression
end rule

% If the dereference is guarded by an if not null, it's safe
rule unmarkIfNotNulls
    replace $ [statement*]
        'if ConstExpr ['constexpr ?] '( InitStatement [init_statement?] Condition [condition] ') 
        ThenPart [nested_statement] 
        ElseIfPart [else_if_statement*] 
        ElsePart [else_statement?] 
        FollowingStatements [statement*]
    by
        'if ConstExpr '( InitStatement Condition ') 
        ThenPart            [unmarkIfNotNullExpressions Condition] 
        ElseIfPart          [unmarkIfNullExpressions Condition]
        ElsePart            [unmarkIfNullExpressions Condition]
        FollowingStatements [unmarkIfNullExpressionsAndNoElse ElsePart Condition]
end rule

% If != null
rule unmarkIfNotNullExpressions Condition [condition]
    construct Nulls [primary_expression*]
        'nullptr 'NULL
    replace $ [unary_expression]
        AuditedUnaryExpression [audited_unary_expression]
    deconstruct AuditedUnaryExpression
        _ [id] '( UnaryExpression [unary_expression] ')
    deconstruct * [postfix_expression] UnaryExpression
        PostfixExpression [postfix_expression]
    construct PointerExpression [postfix_expression]
        PostfixExpression [stripDereferenceArrow]
    deconstruct * [equality_expression] Condition
        PointerExpression '!= Null [primary_expression]
    deconstruct * [primary_expression] Nulls
        Null
    by
        UnaryExpression
end rule

function stripDereferenceArrow
    replace [postfix_expression]
        PointerExpression [postfix_expression] '-> _ ['template ?] _ [id_expression] 
    by
        PointerExpression
end function

% If == null
rule unmarkIfNullExpressions Condition [condition]
    construct Nulls [primary_expression*]
        'nullptr 'NULL
    replace $ [unary_expression]
        AuditedUnaryExpression [audited_unary_expression]
    deconstruct AuditedUnaryExpression
        _ [id] '( UnaryExpression [unary_expression] ')
    deconstruct * [postfix_expression] UnaryExpression
        PostfixExpression [postfix_expression]
    construct PointerExpression [postfix_expression]
        PostfixExpression [stripDereferenceArrow]
    deconstruct * [equality_expression] Condition
        PointerExpression '== Null [primary_expression]
    deconstruct * [primary_expression] Nulls
        Null
    by
        UnaryExpression
end rule

% With no else, if == null affects following statements
function unmarkIfNullExpressionsAndNoElse ElsePart [else_statement?] Condition [condition]
    deconstruct ElsePart
        % empty
    replace [statement*]
        FollowingStatements [statement*]
    by
        FollowingStatements [unmarkIfNullExpressions Condition]
end function
